package MyHttp;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServer {
    //对应的Servlet的容器
    private HashMap<String,Servlet> servletPool =new HashMap();//声明了一个私有实例变量 servletPool，它是一个 HashMap，用于将 String 键映射到 Servlet 值。
    private static class runtimeException extends RuntimeException{ //自定义异常
        public runtimeException() {
        }

        public runtimeException(String message) {
            super(message);
        }

        public runtimeException(String message, Throwable cause) {
            super(message, cause);
        }

        public runtimeException(Throwable cause) {
            super(cause);
        }

        public runtimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }//构造方法 port监听的端口
    public HttpServer(int port) throws IOException {//捕获 IOException，因为创建服务器套接字或接受连接时可能会发生各种问题（例如，端口被占用、权限不足）。
        if(port<1||port>65535){
            throw new runtimeException("端口错误，端口必须在1到65535之间");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        //这是 Java 中用于创建服务器端套接字的类。服务器套接字用于监听传入的客户端连接请求。
        //构造具有50个线程的线程池
    ExecutorService pool= Executors.newFixedThreadPool(50);
        System.out.println("http服务器已经启动了，开始监听端口"+port);
        while(true){
            Socket clientSocket=serverSocket.accept();//阻塞方法，等待客户端连接。返回一个 Socket 对象，表示与客户端的连接。
            //System.out.println("接收到来自客户端的请求");
            //因为存在浏览器多次请求的问题,我们先判断一下接受到的客户端socket是否可用
            if(clientSocket!=null && !clientSocket.isClosed()){
                // 创建一个Runnable任务来处理客户端请求
                Runnable r=()->{
                    try{
                        acceptToClient(clientSocket);
                    } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                };
                pool.submit(Runnable r);
            }
        }
    }
    //获取对应的servlet
    private synchronized Servlet getServlet(String servletName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 1. 构造完整的类名
        servletName="MyHttp."+servletName.substring(0,1).toUpperCase()+servletName.substring(1)+"Servlet";
        // 2. 检查是否已缓存该Servlet
        if(servletPool.containsKey(servletName)){
            return servletPool.get(servletName);
        }
        // 3. 如果未缓存，则通过反射创建新实例
        Class servletClass=Class.forName(servletName);  //根据字符串servletName指定的类名，从类路径（classpath）中加载对应的类。
        Servlet servlet= (Servlet) servletClass.newInstance();//通过反射创建该类的一个新实例。
        //返回的是Object类型，需要强制转换为Servlet类型（因为Servlet类通常实现了javax.servlet.Servlet接口
        // 4. 将新实例存入缓存
        servletPool.put(servletName,servlet);
        return servlet;
    }
    //响应数据给客户端的具体方法
    private void acceptToClient(Socket clientSocket) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        InputStream clientIn=clientSocket.getInputStream();
        OutputStream clientOut =clientSocket.getOutputStream();
        if(clientIn.available()==0){
            writeToClient(clientOut,200,"OK","text/html","<script>window.location.reload();</script>".getBytes());
            return;
        }
        //构造读取客户端请求数据的二进制
        byte[] requestBuff=new byte[clientIn.available()];
        //读取客户端提交上来的数据
        clientIn.read(requestBuff);
        //客户端提交数据转换成String
        String requestStr=new String(requestBuff);
        //System.out.println(requestStr);
        String firstline=requestStr.split("\r\n")[0];
        String requestUrl=firstline.split(" ")[1];
        //动态资源的处理办法
        if(requestUrl.indexOf("/servlet")!=-1){
            String servletName=null;
            if(requestUrl.indexOf("?")!=-1){
                servletName=requestUrl.substring(requestUrl.indexOf("/servlet/")+"/servlet/".length(),requestUrl.indexOf("?"));
            }else{
                servletName=requestUrl.substring(requestUrl.indexOf("/servlet/")+"/servlet/".length());
            }
            servletName=servletName.trim();
            if(servletName==null||servletName.equals("")){
                writeToClient(clientOut,404,"Not Found","text/html","<h1>File Not Found</h1>".getBytes());
                return ;
            }
            //获取对应的servlet的方法

            Servlet servlet=getServlet(servletName);
            String content=servlet.doRequest(requestUrl,requestStr);
            writeToClient(clientOut,200,"OK","text/html",content.getBytes());
            return ;
        }
        //下面是我们的静态资源的处理办法
        if(requestUrl.equals("/favicon.ico")){
            writeToClient(clientOut,200,"OK","text/html","favicon.ico".getBytes());
            return ;
        }
        String contentType="text/html";
        if(requestUrl.indexOf("htm")!=-1||requestUrl.equals("/")) {//表示包含htm或者名为斜杠
            contentType="text/html";
        }else if(requestUrl.indexOf("jpg")!=-1||requestUrl.indexOf("jpeg")!=-1){
            contentType="image/jpeg";
        }else if(requestUrl.indexOf("gif")!=-1){
            contentType="image/gif";
        }else{
            contentType="application/octet-stream";//字节流类型
        }
        String resourcePath=requestUrl.equals("/")?"index.html":requestUrl.substring(1);
//        System.out.println("收到了客户端的请求,浏览器发送上来的请求的数据长度是"+clientIn.available());
//        //System.out.println(firstline);
//        System.out.println(requestUrl);
//        System.out.println("-----------------------------------------");
        //读取资源的内容
        URL resourceUrl=getClass().getClassLoader().getResource(resourcePath);//找到本地文件夹路径
        //如果找不到对应的资源,直接返回404
        if(resourceUrl==null){
            writeToClient(clientOut,404,"Not Found",contentType,"<h1>File Not Found</h1>".getBytes());
            return ;
        }
        //读取资源文件
        byte[] content=null;
        try(BufferedInputStream bis=new BufferedInputStream(new FileInputStream(resourceUrl.getPath()))){
            content=bis.readAllBytes();
        }
        //System.out.println(resourceUrl);
        writeToClient(clientOut,200,"OK",contentType,content);
    }
    private void writeToClient(OutputStream clientOut,int responseCode,String responseDes,String contentType,byte[] content) throws IOException {
        //首先要获取到输出内容到客户端的输出流
        clientOut.write(("HTTP/1.1 "+responseCode+" "+responseDes+"\r\n").getBytes());//\r\n效果是：先回到行首（\r），然后换到下一行（\n）
        clientOut.write(("Content-Type: "+contentType+"; charset=UTF-8\r\n").getBytes());
        //我们先试一试返回一个简单的html：<h1>我是首页</h>
        clientOut.write("\r\n".getBytes());
        clientOut.write(content);
        //强制性把内容刷给客户端
        clientOut.flush();
        clientOut.close();
    }
    public static void main(String args[]) throws IOException {
        new HttpServer(12345);  //localhost:12345
    }
}
